package dslProgramGen;

import dslProgramGen.loopStatementsGenerators.util.TryToGenLoopStatementsUtil;
import methodCallTreeGen.MethodNode;
import methodCallTreeGen.Node;
import methodCallTreeGen.NodeType;
import methodCallTreeGen.TextNode;
import utils.Utils;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static dslProgramGen.ClearRedundantMethodsByMethodHash.clearRedundantMethodDefine;
import static dslProgramGen.TryToGenLoopStatements.tryToGenLoopStatementsForAllMethod;
import static dslProgramGen.dslStatement.DslStatement.*;
import static dslProgramGen.methodCall.GenMethodCallDsl.*;
import static dslProgramGen.newArray.NewArrayObj.*;
import static dslProgramGen.newNormalObject.NewNormalObj.genNewNormalObjAssignToNewLocalRoot;
import static dslProgramGen.util.DslProgramGenUtil.*;
import static methodCallTreeGen.GenMainiClasses.genMainiClassFile;
import static methodCallTreeGen.MethodCallTreeGen.*;
import static methodCallTreeGen.ProcessBeforeMainAndRemovedNodeTraces.*;
import static methodCallTreeGen.ReformatTraces.extractTracesAndReformat;
import static utils.Utils.*;

public class DslProgramGen {

    /**
     * 表示输出为Java程序还是DSL程序，true为Java程序，false为DSL程序
     */
    public static boolean isGenJavaProgram = false;

    /**
     * 表示是否对trace转DSL过程中的空指针进行预判断,如果为true表示对空指针进行预判断，这样不会报空指针错误，但可能会遗漏一些暂时不能处理的复杂程序场景
     */
    public static boolean ignoreNullPointerException = true;

    /**
     * 表示对于多线程程序，是否把所有线程生成的MOP程序合并到一个文件中
     */
    public static boolean isGenOneMopProgram = true;

    /**
     * 表示是否对类型进行强制转换
     */
    public static boolean isCastType = true;

    /**
     * 表示是否开启debug模式，会输出更多信息，相对更慢，默认不开启
     */
    public static boolean isDebug = false;

    /**
     * 将单个函数由trace表示转为DSL语句表示
     * 一个函数的trace可能包含以下内容
     * Add PR
     * TRACES PATCH 1
     * call method f()'s trace
     * TRACES PATCH 2
     * Return
     *
     * @param methodNode 当前待处理的methodNode
     */
    public static void translateTracesToDslInOneMethod(MethodNode methodNode) {
        // 获得函数的新名称
        String dslMethodName = methodNode.newMethodName;
        // 函数修饰符，暂定为 public static
        String dslMethodAccessFlag = "public static";
        // 函数参数列表
        List<String> dslParametersList = new ArrayList<>();
        // 由DSL语句表示的函数内容
        List<String> dslMethodContentList = new ArrayList<>();
        // 函数返回类型
        StringBuilder dslReturnType = new StringBuilder();
        // 函数调用的子函数及其参数列表
        Map<String, List<String>> dslCalledSubMethodName2ParameterList = new HashMap<>();

        // 指定对象id在DSL程序中可以怎样表示出来 <objId, expressions>
        Map<String, WhoPointToThisObjectCollection> objId2objExpressionsMap = new HashMap<>();
        // 给定DSL语句（对象.field 或 对象数组[index]），看看该DSL语句指向哪个对象Id，主要是帮助处理Put与PutA的trace，减少查询时间
        Map<String, String> dslObjFieldToObjIdMap = new HashMap<>(); // <objFieldDsl,objId>
        // local root命名的辅助结构， 对于新建local root例如Student stu，如何命名stu，当前采用 "student"+num的方式表示
        Map<String, Long> classTypeAndNumMap = new HashMap<>();

        int offsetLinesNum = 0; // 如果当前待处理的Node为TextNode，前面的Node可能涉及处理当前TextNode的部分行，处理当前TextNode的时候要偏移offsetLinesNum行
        // 对methodNode下面的每个Node进行处理，生成该函数的DSL语句
        for (int i = 0; i < methodNode.contentNodeList.size(); i++) {
            // 当前待处理的Node
            Node currentProcessedNode = methodNode.contentNodeList.get(i);
            // 如果当前Node为TextNode
            if (currentProcessedNode.nodeType == NodeType.TEXT) {
                offsetLinesNum = processOneTextNodeInTraceToDslProcess((TextNode) currentProcessedNode, offsetLinesNum, dslParametersList, dslMethodContentList, dslReturnType,
                        objId2objExpressionsMap, dslObjFieldToObjIdMap, classTypeAndNumMap);
            } else {
                // 如果当前子Node为MethodNode，表示函数调用语句
                int offsetLinesNumTemp = processOneSubMethodNodeInTraceToDslProcess((MethodNode) currentProcessedNode, dslMethodContentList,
                        objId2objExpressionsMap,
                        dslObjFieldToObjIdMap,
                        classTypeAndNumMap,
                        dslCalledSubMethodName2ParameterList);
                if (offsetLinesNumTemp == -1) {
                    // 表示调用的是对象初始化函数
                    // offsetLinesNum值不变
                } else {
                    offsetLinesNum = offsetLinesNumTemp;
                }
            }
        }
        int MatchIndex = -1;
        int ObjArrayIndex = -1;
        if(!methodNode.isChangedForIndex) {
            //查找内容中是否含有唯一符合Obj数组正则表达式的
            for (int i = 0; i < dslMethodContentList.size(); i++) {
                String tmp = dslMethodContentList.get(i);
                Matcher m = TryToGenLoopStatementsUtil.ObjArrayMatcher.matcher(tmp);
                if (m.find()) {
                    if (MatchIndex < 0) {
                        ObjArrayIndex = Integer.valueOf(m.group(1));
                        MatchIndex = i;
                    } else {
                        MatchIndex = -1;
                        break;
                    }
                }
            }
            if (MatchIndex > -1) {
                if(setIndexAsPar(methodNode, ObjArrayIndex) > -1) {
                    //进行替换
                    String tmp = dslMethodContentList.get(MatchIndex);
                    tmp = tmp.replaceFirst("\\[(\\d+)]", "\\[iNdex]");
                    dslMethodContentList.set(MatchIndex, tmp);
                    dslParametersList.add("int iNdex");
                    methodNode.isChangedForIndex = true;
                }
            }
        }
        methodNode.dslExpressedMethod = new DslExpressedMethod(dslMethodAccessFlag, dslMethodName, dslParametersList, dslMethodContentList, dslReturnType.toString(), dslCalledSubMethodName2ParameterList);
    }

    /**
     * 为当前methodeNode的调用语句添加Index参数并向上继续处理
     * @param methodNode
     * @param ObjArrayIndex
     * @return
     */
    public static int setIndexAsPar(MethodNode methodNode, int ObjArrayIndex){
        MethodNode parentNode = (MethodNode) methodNode.parentNode;
        if(parentNode == null)
            return -1;
        //判断是否同类
        String[] classArray1 = methodNode.newMethodName.split("__");
        String[] classArray2 = parentNode.newMethodName.split("__");
        boolean isSameClass = classArray1[0].equals(classArray2[0]);
        //进行替换与添加
        DslExpressedMethod parentDsl = parentNode.dslExpressedMethod;
        for(int i =0;i < parentDsl.methodContentList.size();i++){
            String tmp = parentDsl.methodContentList.get(i);
            if(tmp.contains(methodNode.newMethodName+"(")){
                if(isSameClass && !parentNode.isChangedForIndex){
                    if(setIndexAsPar(parentNode, ObjArrayIndex)> -1) {
                        tmp = tmp.replaceFirst("\\);", ", iNdex\\);");
                        parentDsl.methodContentList.set(i, tmp);
                        parentDsl.parameters.add("int iNdex");
                        parentNode.isChangedForIndex = true;
                        break;
                    } else {
                        changeVarName(ObjArrayIndex, parentDsl, i, tmp);
                        break;
                    }
                } else {
                    changeVarName(ObjArrayIndex, parentDsl, i, tmp);
                    break;
                }
            }
        }
        return 0;
    }

    public static void changeVarName(int ObjArrayIndex, DslExpressedMethod parentDsl, int i, String tmp) {
        Matcher m = Pattern.compile("\\)([^)]+)\\);").matcher(tmp);
        if(m.find()){
            String name = m.group(1);
            String className = name.replaceFirst("_\\d+","");
            String declare = className + " " +name + " =";
            int lastIndex = i;
            //检查后续是否还有使用
            for (int j = parentDsl.methodContentList.size()-1; j > i ; j--) {
                String s = parentDsl.methodContentList.get(j);
                if(s.contains(name+",") || s.contains(name+" ") || s.contains(name+")")){
                    lastIndex = j;
                    break;
                }
            }
            if(lastIndex == i){
                //检查声明语句是否相邻
                if(i>1){
                    String s = parentDsl.methodContentList.get(i-2);
                    if(s.toLowerCase().contains(declare)){
                        for (int j = i-2; j < i+1; j++) {
                            String s1 = parentDsl.methodContentList.get(j);
                            s1 = s1.replace(name,className+"_11111");
                            parentDsl.methodContentList.set(j,s1);
                        }
                    } else if(i > 2){
                        String s1 = parentDsl.methodContentList.get(i-3);
                        if(s1.toLowerCase().contains(declare)){
                            for (int j = i-3; j < i+1; j++) {
                                String s2 = parentDsl.methodContentList.get(j);
                                s2 = s2.replace(name,className+"_11111");
                                parentDsl.methodContentList.set(j,s2);
                            }
                        }
                    }
                }
            }
        }
        tmp =  parentDsl.methodContentList.get(i).replaceFirst("\\);", ", "+ObjArrayIndex+"\\);");
        parentDsl.methodContentList.set(i, tmp);
        return;
    }

    /**
     * 在处理单个函数 trace转DSL语句的过程中，处理函数子节点为TextNode的情形
     *
     * @param currentTextNode         待处理的TextNode节点
     * @param offsetLinesNum          当前TextNode节点从第几行开始处理
     * @param dslParametersList       由DSL语句表示的MethodNode 参数列表
     * @param dslMethodContentList    由DSL语句表示的MethodNode 内容列表
     * @param dslReturnType           由DSL语句表示的MethodNode 返回类型
     * @param objId2objExpressionsMap 指定对象id在DSL程序中可以怎样表示出来 <objId, expressions>
     * @param dslObjFieldToObjIdMap   给定DSL语句（对象.field 或 对象数组[index]），看看该DSL语句指向哪个对象Id，主要是帮助处理Put与PutA的trace，减少查询时间
     * @param classTypeAndNumMap      local root命名的辅助结构， 对于新建local root例如Student stu，如何命名stu，当前采用 "student"+num的方式表示
     * @return 处理单个TextNode可能跨越处理了其他TextNode的开始行，返回处理其他TextNode涉及的行数
     */
    public static int processOneTextNodeInTraceToDslProcess(TextNode currentTextNode, int offsetLinesNum, List<String> dslParametersList,
                                                            List<String> dslMethodContentList, StringBuilder dslReturnType,
                                                            Map<String, WhoPointToThisObjectCollection> objId2objExpressionsMap,
                                                            Map<String, String> dslObjFieldToObjIdMap,
                                                            Map<String, Long> classTypeAndNumMap) {
        // 当前待处理的trace行号，正常的话从0开始
        int currentProcessedLineNum = offsetLinesNum;
        // 获得currentTextNode所在的函数，即当前TextNode的父节点
        MethodNode currentMethodNode = (MethodNode) currentTextNode.parentNode;
        // 获得当前处理的method的所有不为null的Add PR行
        List<String> allNotNullAddPrTracesInThisMethod = getAllNotNullAddPrTracesFromMethodNode(currentMethodNode);
        // 表示当前函数的父函数 调用的子函数名称及其参数列表，key表示调用的子函数名称，value表示子函数的参数列表
        Map<String, List<String>> parentMethodNodeCalledSubMethodName2ParameterList = null;
        // 如果 调用当前函数的函数，即当前函数的父函数 不为null
        if (currentTextNode.parentNode.parentNode != null) {
            DslExpressedMethod dslExpressedMethodOfParentMethodNode = ((MethodNode) currentTextNode.parentNode.parentNode).dslExpressedMethod;
            if (dslExpressedMethodOfParentMethodNode != null) {
                parentMethodNodeCalledSubMethodName2ParameterList = dslExpressedMethodOfParentMethodNode.calledSubMethodName2ParameterList;
            }
        }

        while (currentProcessedLineNum < currentTextNode.tracesList.size()) {
            // 获得当前处理的行
            String line = currentTextNode.tracesList.get(currentProcessedLineNum);
//            System.out.println(line);
            // 获得这一行trace的操作名称
            String lineComand = Utils.RegularExpressionProcess(line, "\\[gc,heap\\s+\\] (.*?) (H|H1):");
            if (lineComand == null) {
                // 下面的switch语句并没有处理 NewM C:[[Ljava/lang/invoke/LambdaForm$Name;, D:5 10 , T:main, M:java/lang/invoke/LambdaForm.<clinit> ()V
                // 不希望NewM也打印出来
                String newMClassName = Utils.RegularExpressionProcess(line, "\\[gc,heap\\s+\\] NewM C:(.*?),"); // 获得NewM中的Class名称
                if (newMClassName == null) {
                    // 表示这行语句不是NewM
                    System.err.println("ERROR_lineComand_NULL:" + line);
                }
                currentProcessedLineNum += 1;
                continue;
            }

            switch (lineComand) {
                case "Add PR":
                    // 表示该函数获取的参数
                    // Add PR H:0x000000004be490da, C:java/lang/module/ModuleDescriptor, T:main, M:java/util/HashMap.put (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
                    // 获得PR的id
                    String prObjId = Utils.RegularExpressionProcess(line, "H:(.*?)(,| )");
                    // PR可能为NULL
                    if ("NULL".equals(prObjId)) {
                        // 如果prObjId为NULL
                        // Add PR H:NULL, C:NULL, T:main, M:Test.myFunctionReturnMethod ()LStudent;
                        // 不处理
                    } else {
                        // 如果prObjId不为NULL
                        // 获得PR的类型
                        String prObjType = Utils.RegularExpressionProcess(line, "C:(.*?),");
                        prObjType = renameClassNameReplaceSlashWithUnderlineAndRenameObjectClassName(prObjType);
                        // 如果要创建local root，先获得local root的序号
                        long prNum = getLocalRootNum(prObjType, classTypeAndNumMap);
                        String leftStatement;
                        String prVariableName;
                        // 判断local root是不是数组类型
                        if (isArrayType(prObjType)) {
                            // local root为数组类型
                            // 获得数组elementType
                            String elementType = getElementTypeFromArrayObjectType(prObjType);
                            // 获得数组对象的维度
                            int dimNum = getDimNumFromArrayObjectType(prObjType);
                            // 组成local root的全名 小写(element类型)_array_维度_prNum
                            prVariableName = elementType.toLowerCase() + "_array_" + dimNum + "_" + prNum;

                            String squareBracket = "";
                            for (int i = 0; i < dimNum; i++) {
                                squareBracket += "[]";
                            }

                            leftStatement = elementType + squareBracket + " " + prVariableName;
                        } else {
                            // local root为普通对象类型
                            // 组成local root的全名 小写(类型)_prNum
                            prVariableName = prObjType.toLowerCase() + "_" + prNum;

                            leftStatement = prObjType + " " + prVariableName;
                        }
                        // 将变量添加到函数变量列表中 例如 Student prVariableName
                        dslParametersList.add(leftStatement);
                        // 获得当前处理的Add PR行是第几个参数（在非NULL的列表中）
                        int indexOfCurrentAddPrLineInAllNotNullAddPrList = allNotNullAddPrTracesInThisMethod.indexOf(line);
                        // 获得该函数的父函数调用该函数时在该index传入的参数，若为"null"，则不用在objId2objExpressionsMap中添加对应值
                        if (parentMethodNodeCalledSubMethodName2ParameterList != null) {
                            // 获得父函数调用该函数传入的参数列表
                            List<String> calledSubMethodParameterList = parentMethodNodeCalledSubMethodName2ParameterList.get(currentMethodNode.newMethodName);
                            // 如果父函数调用该函数在该index位置传入的参数为null，则不把该prObjId加入到局部变量表中
                            if ("null".equals(calledSubMethodParameterList.get(indexOfCurrentAddPrLineInAllNotNullAddPrList))) {
                                currentProcessedLineNum += 1;
                                break;
                            }
                        }

                        // 将prObjId添加到objId2objExpressionsMap
                        WhoPointToThisObjectCollection whoPointToThisObjectCollection = new WhoPointToThisObjectCollection(prObjType, prObjId);
                        whoPointToThisObjectCollection.lrAndPrPointToThisObjectList.add(prVariableName);
                        objId2objExpressionsMap.put(prObjId, whoPointToThisObjectCollection);
                    }

                    currentProcessedLineNum += 1;
                    break;
                case "New": // 新建普通对象或一维数组
                    // 普通对象 New H:0x000000006be78a46, S:4, C:java/util/HashMap$Node, T:main, M:java/util/HashMap.newNode (ILjava/lang/Object;Ljava/lang/Object;Ljava/util/HashMap$Node;)Ljava/util/HashMap$Node;
                    // 一维数组 New H:0x000000000ea1a8d5, S:4, C:[Ljava/lang/Stri, D:3, T:main, M:sun/nio/cs/StandardCharsets.aliases_UTF_16LE ()[Ljava/lang/String;
                    // 先看看trace中 D: 有没有值，以此区分new的是普通对象还是一维数组
                    String oneDimArrayLength = Utils.RegularExpressionProcess(line, "D:(.*?),");
                    if (oneDimArrayLength == null) {
                        // 新建的是普通对象
                        // 获得对象的id
                        String newObjectId = Utils.RegularExpressionProcess(line, "H:(.*?),");
                        // 获得对象的类型
                        String newObjectType = Utils.RegularExpressionProcess(line, "C:(.*?),");
                        newObjectType = renameClassNameReplaceSlashWithUnderlineAndRenameObjectClassName(newObjectType);
                        // 对于新建普通对象，直接默认给它生成一个新建局部变量例如 Student stu = new Student();
                        genNewNormalObjAssignToNewLocalRoot(newObjectId, newObjectType, dslMethodContentList,
                                objId2objExpressionsMap,
                                classTypeAndNumMap);
                        currentProcessedLineNum += 1;
                    } else {
                        // 新建的是一维数组对象
                        // 一维数组 New H:0x000000000ea1a8d5, S:4, C:[Ljava/lang/Stri, D:3, T:main, M:sun/nio/cs/StandardCharsets.aliases_UTF_16LE ()[Ljava/lang/String;
                        // 新建一维数组对象要与之后的某行A一起构成一条DSL语句
                        // 数组的元素个数 即 oneDimArrayLength
                        // 获得新建数组对象的id
                        String newArrayObjectId = Utils.RegularExpressionProcess(line, "H:(.*?),");
                        // 获得新建数组对象的类型 [LStudent; [I [Ljava/lang/Object;
                        String newArrayObjectType = Utils.RegularExpressionProcess(line, "C:(.*?),");
                        newArrayObjectType = renameClassNameReplaceSlashWithUnderlineAndRenameObjectClassName(newArrayObjectType);
                        // 直接生成Student[] stu = new Student[i]语句;

                        // 生成DSL语句，例如 Student[][] stu = new Student[i][];
                        // 如果要创建local root，先获得local root的序号
                        long prNum = getLocalRootNum(newArrayObjectType, classTypeAndNumMap);
                        // 获得其element类型
                        String elementType = getElementTypeFromArrayObjectType(newArrayObjectType);
                        // 获得新建数组对象的维度
                        int dimNum = getDimNumFromArrayObjectType(newArrayObjectType);
                        // 构造等号右边的 []表达 squareBracket比数组维度少一个[]
                        String squareBracket = "";
                        for (int i = 0; i < dimNum - 1; i++) {
                            squareBracket += "[]";
                        }

                        // 组成local root的全名 小写(elementType)_array_维度_prNum
                        String prVariableName = elementType.toLowerCase() + "_array_" + dimNum + "_" + prNum;
                        String leftStatement = elementType + "[]" + squareBracket + " " + prVariableName;
                        String rightStatement = genNewArrayObjEqualSignRightStatement(newArrayObjectType, oneDimArrayLength);
                        String dslStatement = leftStatement + " = " + rightStatement;
                        // 将该dsl语句加入函数体中
                        dslMethodContentList.add(dslStatement);

                        // 将newObjectId添加到objId2objExpressionsMap
                        WhoPointToThisObjectCollection whoPointToThisObjectCollection = new WhoPointToThisObjectCollection(newArrayObjectType, newArrayObjectId);
                        whoPointToThisObjectCollection.lrAndPrPointToThisObjectList.add(prVariableName);
                        objId2objExpressionsMap.put(newArrayObjectId, whoPointToThisObjectCollection);

                        // 下一行trace即将被处理
                        currentProcessedLineNum += 1;
                    }
                    break;
                case "NewL":
                    // 多维数组构建过程是一层一层往下构建，这里区分了最外层的数组对象与内部的数组对象
                    // NewL H:0x0000000033963f9a, C:[[[LStudent;, R:3, S:2, T:main
                    // 获得最外层数组元素个数
                    String outerArrayLengthOfNewL = Utils.RegularExpressionProcess(line, "S:(.*?),");
                    // 获得最外层数组元素类型
                    String outerArrayType = Utils.RegularExpressionProcess(line, "C:(.*?),");
                    outerArrayType = renameClassNameReplaceSlashWithUnderlineAndRenameObjectClassName(outerArrayType);
                    // 获得对象的id
                    String outerObjectId = Utils.RegularExpressionProcess(line, "H:(.*?),");
                    // 获得接下来的trace行与最外层数组匹配的行，例如 匹配的操作可能为Put, PutA, Add LR, Return
                    // 还有可能找不到匹配的行，返回-1，则表示原程序调用了f(new array[2][3])
                    int matchLineNum = findMatchLineForOuterArray(outerObjectId, currentProcessedLineNum + 1, currentTextNode.tracesList);
                    if (matchLineNum == -1 && hasNextSiblingNodeOfCurrentNode(currentTextNode) && getNextSiblingNodeOfCurrentNode(currentTextNode).nodeType == NodeType.METHOD) {
                        // 表示 f(new array[][])
                        // 先构造一个临时local root表示新建最外层数组对象,Student array[][]
                        genNewArrayObjAssignToTempLocalRootForMethodParameter(outerArrayType,
                                outerObjectId,
                                outerArrayLengthOfNewL,
                                dslMethodContentList,
                                objId2objExpressionsMap,
                                classTypeAndNumMap);
                        // 处理新建多维数组中的 新建lower数组对象
                        genNewLowerArrayObjInMultiArray(currentTextNode.tracesList.subList(currentProcessedLineNum + 1, currentTextNode.tracesList.size()),
                                dslMethodContentList,
                                objId2objExpressionsMap,
                                dslObjFieldToObjIdMap);
                        return 0;
                    } else {
                        // 直接生成Student[][] stus = new Array[i][]
                        // 生成DSL语句，例如 Student[][] stu = new Student[i][];
                        // 如果要创建local root，先获得local root的序号
                        long prNum = getLocalRootNum(outerArrayType, classTypeAndNumMap);
                        // 获得其element类型
                        String elementType = getElementTypeFromArrayObjectType(outerArrayType);
                        // 获得新建数组对象的维度
                        int dimNum = getDimNumFromArrayObjectType(outerArrayType);
                        // 构造等号右边的 []表达 squareBracket比数组维度少一个[]
                        String squareBracket = "";
                        for (int i = 0; i < dimNum - 1; i++) {
                            squareBracket += "[]";
                        }

                        // 组成local root的全名 小写(elementType)_array_维度_prNum
                        String prVariableName = elementType.toLowerCase() + "_array_" + dimNum + "_" + prNum;
                        String leftStatement = elementType + "[]" + squareBracket + " " + prVariableName;
                        String rightStatement = genNewArrayObjEqualSignRightStatement(outerArrayType, outerArrayLengthOfNewL);
                        String dslStatement = leftStatement + " = " + rightStatement;
                        // 将该dsl语句加入函数体中
                        dslMethodContentList.add(dslStatement);

                        // 将newObjectId添加到objId2objExpressionsMap
                        WhoPointToThisObjectCollection whoPointToThisObjectCollection = new WhoPointToThisObjectCollection(outerArrayType, outerObjectId);
                        whoPointToThisObjectCollection.lrAndPrPointToThisObjectList.add(prVariableName);
                        objId2objExpressionsMap.put(outerObjectId, whoPointToThisObjectCollection);

                        // 处理新建多维数组中的 新建lower数组对象
                        genNewLowerArrayObjInMultiArray(currentTextNode.tracesList.subList(currentProcessedLineNum + 1, matchLineNum),
                                dslMethodContentList,
                                objId2objExpressionsMap,
                                dslObjFieldToObjIdMap);
                        currentProcessedLineNum = matchLineNum;
                    }
                    break;
                case "Put": // 修改普通对象引用字段，例如原来指向A，现在指向B
                    // A.field = DslStatement;
                    // 区分Put是修改静态字段还是实例字段
                    // 修改静态字段例子 Put H1:0x0000000046cdf8bd, C1:Student, IS:true, H2:0x000000000f0c8a99, C2:Course, F:112, T:main, M:java/lang/module/Configuration.<init> (Ljava/util/List;Ljava/lang/module/Resolver;)V
                    // 修改实例字段例子 Put H1:0x000000003de64145, C1:java/lang/module/Configuration, IS:false, H2:0x000000001ecd979e, C2:java/util/ImmutableCollections$List12, F:12, T:main, M:java/lang/module/Configuration.<init> (Ljava/util/List;Ljava/lang/module/Resolver;)V
                    String isStatic = Utils.RegularExpressionProcess(line, "IS:(.*?),");
                    if (isStatic.equals("false")) {
                        // 修改的是实例字段
                        genDslStatementAssignToObjField(line,
                                false,
                                dslMethodContentList,
                                objId2objExpressionsMap,
                                dslObjFieldToObjIdMap);
                    } else {
                        // 修改的是静态字段
                        genDslStatementAssignToObjField(line,
                                true,
                                dslMethodContentList,
                                objId2objExpressionsMap,
                                dslObjFieldToObjIdMap);
                    }
                    currentProcessedLineNum += 1;
                    break;
                case "PutA": // 修改数组对象中某一元素，例如元素原来指向A，现在指向B
                    // PutA H1:0x00000000468f8309, C1:[Ljava/util/Map$Entry;, H2:0x000000004d425872, C2:java/util/KeyValueHolder, O:63, T:main, M:java/lang/module/Configuration.<init> (Ljava/util/List;Ljava/lang/module/Resolver;)V
                    // Array[index] = DslStatement;
                    genDslStatementAssignToArrayIndex(line,
                            dslMethodContentList,
                            objId2objExpressionsMap,
                            dslObjFieldToObjIdMap);
                    currentProcessedLineNum += 1;
                    break;
                case "Return":
                    // 1. 确定函数定义中的返回值类型
                    // 从函数描述符中获得函数返回类型
                    String methodReturnType = getMethodReturnType((MethodNode) currentTextNode.parentNode);
                    methodReturnType = renameClassNameReplaceSlashWithUnderlineAndRenameObjectClassName(methodReturnType);
                    // Return H:0x000000001e80bfe8, C:java/lang/ThreadGroup, T:main
                    // 获得trace中返回对象类型
                    String returnTypeInTrace = Utils.RegularExpressionProcess(line, "C:(.*?),");
                    returnTypeInTrace = renameClassNameReplaceSlashWithUnderlineAndRenameObjectClassName(returnTypeInTrace);
                    if (methodReturnType.startsWith("L")) {
                        // 返回值是普通对象类型
                        // 看看returnTypeInTrace是否为NULL，如果不为NULL，则以returnTypeInTrace为函数定义中的返回值，否则取函数描述符中的返回值
                        if ("NULL".equals(returnTypeInTrace)) {
                            // 函数返回值定义以函数描述符为准
                            // 清除普通对象类型前面的L与后面的; input: Ljava/lang/String; output: java/lang/String
                            methodReturnType = refineClassName(methodReturnType);
                            dslReturnType.append(methodReturnType);
                        } else {
                            // 函数返回值定义以trace为准

                            if (isArrayType(returnTypeInTrace)) {
                                // 获得其element类型
                                String elementType = getElementTypeFromArrayObjectType(returnTypeInTrace);
                                // 获得数组类型的维度
                                int dimNum = getDimNumFromArrayObjectType(returnTypeInTrace);
                                // 构造[]表达
                                String squareBracket = "";
                                for (int i = 0; i < dimNum; i++) {
                                    squareBracket += "[]";
                                }
                                // 重新构造返回类型
                                returnTypeInTrace = elementType + squareBracket;
                            } else if (returnTypeInTrace.startsWith("L")) {
                                returnTypeInTrace = refineClassName(returnTypeInTrace);
                            }

                            dslReturnType.append(returnTypeInTrace);
                        }
                    } else if (isArrayType(methodReturnType)) {
                        // 返回类型是数组类型
                        // 看看returnTypeInTrace是否为NULL，如果不为NULL，则以returnTypeInTrace为函数定义中的返回值，否则取函数描述符中的返回值
                        if ("NULL".equals(returnTypeInTrace)) {
                            // 取函数描述符中的返回值为函数定义的返回值类型
                            // 获得其element类型
                            String elementType = getElementTypeFromArrayObjectType(methodReturnType);
                            // 获得数组类型的维度
                            int dimNum = getDimNumFromArrayObjectType(methodReturnType);
                            // 构造[]表达
                            String squareBracket = "";
                            for (int i = 0; i < dimNum; i++) {
                                squareBracket += "[]";
                            }
                            // 重新构造返回类型
                            String returnArrayType = elementType + squareBracket;
                            dslReturnType.append(returnArrayType);
                        } else {
                            // 取returnTypeInTrace为函数定义的返回值类型
                            // 获得其element类型
                            String elementType = getElementTypeFromArrayObjectType(returnTypeInTrace);
                            // 获得数组类型的维度
                            int dimNum = getDimNumFromArrayObjectType(returnTypeInTrace);
                            // 构造[]表达
                            String squareBracket = "";
                            for (int i = 0; i < dimNum; i++) {
                                squareBracket += "[]";
                            }
                            // 重新构造返回类型
                            String returnArrayType = elementType + squareBracket;
                            dslReturnType.append(returnArrayType);
                        }
                    } else {
                        // 返回值为基础数据类型或void
                        methodReturnType = "void";
                        dslReturnType.append("void");
                    }
                    // 2. 生成函数内的return语句
                    // Return H:0x000000001e80bfe8, C:java/lang/ThreadGroup, T:main
                    // 获得返回对象id
                    String returnObjId = Utils.RegularExpressionProcess(line, "H:(.*?),");
                    if ("NULL".equals(returnObjId)) {
                        // 如果函数的描述符有返回值
                        if (!"void".equals(methodReturnType)) {
                            // 如果返回值为null
                            // 生成DSL语句
                            String returnDslStatement = "return null;";
                            // 将DSL语句添加到函数体中
                            dslMethodContentList.add(returnDslStatement);
                        }
                        // 对main线程调用其他线程的start函数单独处理生成spawn语句
                        // 例如 [25.483s][info][gc,heap     ] Return H:NULL, C:NULL, T:main, M:java/lang/Thread.start ()V
                        if (isMainThreadCalledOtherThreadStartMethod(currentTextNode)) {
                            String spawnDslStatement = genSpawnDslStatement(currentTextNode, objId2objExpressionsMap);
                            dslMethodContentList.add(spawnDslStatement);
                        }
                    } else {
                        // 首先在全局变量表中找一下有没有这个returnObjId
                        GlobalVarTableRecord globalVarTableRecord = globalVarTable.get(returnObjId);
                        if (globalVarTableRecord != null) {
                            // 在全局变量表中找到了
                            // 生成DSL语句
                            String returnDslStatement = "return " + globalVarTableRecord.dslExpr + ";";
                            // 将DSL语句添加到函数体中
                            dslMethodContentList.add(returnDslStatement);
                        } else if (objId2objExpressionsMap.get(returnObjId) == null) {
                            // 生成DSL语句
                            String returnDslStatement = "return null;";
                            // 将DSL语句添加到函数体中
                            dslMethodContentList.add(returnDslStatement);
                        }
                        // 返回值不为null
                        else if (objId2objExpressionsMap.get(returnObjId).newForReturnTempLocalRootList.size() != 0) {
                            // 表示return new Student(); 创建的临时local root newForReturnTempLocalRootList列表不为空
                            String tempLocalRootForReturn = objId2objExpressionsMap.get(returnObjId).newForReturnTempLocalRootList.get(0);
                            // 生成DSL语句
                            String returnDslStatement = "return " + tempLocalRootForReturn + ";";
                            // 将DSL语句添加到函数体中
                            dslMethodContentList.add(returnDslStatement);
                            // 删除newForReturnTempLocalRootList的内容
                            objId2objExpressionsMap.get(returnObjId).newForReturnTempLocalRootList.remove(0);
                        } else if (objId2objExpressionsMap.get(returnObjId).methodReturnForReturnTempLocalRootList.size() != 0) {
                            // 表示return f(); 创建的临时local root methodReturnForReturnTempLocalRootList列表不为空
                            String tempLocalRootForReturn = objId2objExpressionsMap.get(returnObjId).methodReturnForReturnTempLocalRootList.get(0);
                            // 生成DSL语句
                            String returnDslStatement = "return " + tempLocalRootForReturn + ";";
                            // 将DSL语句添加到函数体中
                            dslMethodContentList.add(returnDslStatement);
                            // 删除newForReturnTempLocalRootList的内容
                            objId2objExpressionsMap.get(returnObjId).methodReturnForReturnTempLocalRootList.remove(0);
                        } else if (objId2objExpressionsMap.get(returnObjId).lrAndPrPointToThisObjectList.size() != 0) {
                            // 表示return stu;
                            // 获得返回对象的dsl表示
                            String dslExpressionOfReturnObj = getDslExpressionOfObj(returnObjId, objId2objExpressionsMap);
                            // 函数定义返回值类型可能和return语句返回值类型不一致，比如return语句返回值类型为父类，而函数定义返回值类型为子类，可能需要强制类型转换
                            String returnDslStatement = null;
                            if (DslProgramGen.isCastType) {
                                // 进行强制类型转换
                                String dslReturnTypeString = dslReturnType.toString();
                                returnDslStatement = "return " + "(" + dslReturnTypeString + ")" + "(" + dslExpressionOfReturnObj + ")" + ";";
                            } else {
                                // 不需要进行强制类型转换
                                // 生成DSL语句
                                returnDslStatement = "return " + dslExpressionOfReturnObj + ";";
                            }
                            // 将DSL语句添加到函数体中
                            dslMethodContentList.add(returnDslStatement);
                        } else if (objId2objExpressionsMap.get(returnObjId).fieldOfObjExpressionsPointToThisObjectList.size() != 0) {
                            // 获得返回对象的dsl表示
                            String dslExpressionOfReturnObj = getDslExpressionOfObj(returnObjId, objId2objExpressionsMap);
                            // 函数定义返回值类型可能和return语句返回值类型不一致，比如return语句返回值类型为父类，而函数定义返回值类型为子类，可能需要强制类型转换
                            String returnDslStatement = null;
                            if (DslProgramGen.isCastType) {
                                // 进行强制类型转换
                                String dslReturnTypeString = dslReturnType.toString();
                                returnDslStatement = "return " + "(" + dslReturnTypeString + ")" + "(" + dslExpressionOfReturnObj + ")" + ";";
                            } else {
                                // 不需要进行强制类型转换
                                // 生成DSL语句
                                returnDslStatement = "return " + dslExpressionOfReturnObj + ";";
                            }
                            // 将DSL语句添加到函数体中
                            dslMethodContentList.add(returnDslStatement);
                        } else if (objId2objExpressionsMap.get(returnObjId).getTracesRecordList.size() != 0) {
                            // 表示return A.field; 创建的get语句信息列表不为空
                            String tempGetTracesObj = objId2objExpressionsMap.get(returnObjId).getTracesRecordList.get(0);
                            // 函数定义返回值类型可能和return语句返回值类型不一致，比如return语句返回值类型为父类，而函数定义返回值类型为子类，可能需要强制类型转换
                            String returnDslStatement = null;
                            if (DslProgramGen.isCastType) {
                                // 进行强制类型转换
                                String dslReturnTypeString = dslReturnType.toString();
                                returnDslStatement = "return " + "(" + dslReturnTypeString + ")" + "(" + tempGetTracesObj + ")" + ";";
                            } else {
                                // 不需要进行强制类型转换
                                // 生成DSL语句
                                returnDslStatement = "return " + tempGetTracesObj + ";";
                            }

                            // 将DSL语句添加到函数体中
                            dslMethodContentList.add(returnDslStatement);
                            // 删除getTracesRecordList的内容
                            objId2objExpressionsMap.get(returnObjId).getTracesRecordList.remove(0);
                        } else {

                        }
                    }
                    return 0;
                // 下面连着的几种情况目前主要是jvm内部使用，不明确和应用程序的关系，暂不处理
                case "Add Class root":
                    // Add Class root H:0x000000003282806f, D:'app', S:15, C:java/lang/Class
                case "Add Set root":
                    // Add Set root H:0x00000000251a69d7 Set:VM Global Size:14 C:java/lang/Class, A:0x00007f3b4c09d040
                case "Del Class root":
                    // Del Class root H:0x000000007344699f, D:unknown
                case "Del Set root":
                    // Del Set root H:Hashcode Set:Oopstorageset名称 A:address
                case "Del LR":
                    // Del LR H:0x000000001e80bfe8, C:java/lang/ThreadGroup, T:main, M:java/lang/ThreadGroup.addUnstarted ()V
                case "Rep Set root":
                    // Rep Set root H:Hashcode A:addressto H:Hashcode
                    currentProcessedLineNum += 1;
                    break;
                case "Add LR": // 这个地方需要注意Add表示两种含义，一是新增一个GC root，二是修改GC root（比如原来gc root指向A,现在指向B），具体含义由R：部分是否为null确定
                    // 如果R部分为null表示新增GC root，如果R部分不为null，表示修改gc root
                    // Add LR H:0x000000004edde6e5, C:java/lang/ThreadGroup, T:main, R: NULL, M:java/lang/ThreadGroup.addUnstarted ()V
                    // Add LR H:0x000000000d716361, C:java/lang/Class, T:main, R:0x000000004459eb14, M:java/util/ImmutableCollections$MapN.<init> ([Ljava/lang/Object;)V
                    genDslStatementAssignToLocalRoot(line,
                            dslMethodContentList,
                            objId2objExpressionsMap,
                            classTypeAndNumMap);
                    currentProcessedLineNum += 1;
                    break;
                case "Get":
                    //[0.020s][info][gc,heap     ] Get H1:0x000000004dc63996, C1:java/lang/Module, IS:true, H2:0x00000000179d3b25, C2:java/lang/Module, F:ALL_UNNAMED_MODULE, T:main, M:java/lang/Module.<clinit> ()V
                    //H2 = H1.F
                    String getStatic = Utils.RegularExpressionProcess(line, "IS:(.*?),");
                    String h2NewName;
                    // 获得C1的类名称
                    String c1ClassName = Utils.RegularExpressionProcess(line, "C1:(.*?),");
                    c1ClassName = renameClassNameReplaceSlashWithUnderlineAndRenameObjectClassName(c1ClassName);
                    // 获得C2的类名称
                    String c2ClassName = Utils.RegularExpressionProcess(line, "C2:(.*?),");
                    c2ClassName = renameClassNameReplaceSlashWithUnderlineAndRenameObjectClassName(c2ClassName);
                    // 获得字段名
                    String fieldName = Utils.RegularExpressionProcess(line, "F:(.*?),");
                    // 获得H1的id
                    String h1ObjId = Utils.RegularExpressionProcess(line, "H1:(.*?),");
                    // 获得H2的id
                    String h2ObjId = Utils.RegularExpressionProcess(line, "H2:(.*?),");
                    // 如果获取null，不做处理
                    if (h2ObjId.equals("NULL")) {
                        currentProcessedLineNum += 1;
                        break;
                    }


                    if (getStatic.equals("false")) {
                        // 获得H1对象的dsl表示
                        if (tryToGetDslExpressionOfObj(h1ObjId, objId2objExpressionsMap) == null) {
                            // 如果没有，不处理
                            currentProcessedLineNum += 1;
                            break;
                        }
                        String h1Name = getDslExpressionOfObj(h1ObjId, objId2objExpressionsMap);

                        //修改实例字段
//                        WhoPointToThisObjectCollection tempList = objId2objExpressionsMap.get(h1ObjId);
//                        String h1Name = null;
//                        if (tempList == null) { // 如果tempList为null，不处理
//                            currentProcessedLineNum += 1;
//                            break;
//                        }
//                        //获取h1名称
//                        if (tempList.lrAndPrPointToThisObjectList.size() > 0) {
//                            h1Name = tempList.lrAndPrPointToThisObjectList.get(0);
//                        } else if (tempList.fieldOfObjExpressionsPointToThisObjectList.size() > 0) {
//                            h1Name = tempList.fieldOfObjExpressionsPointToThisObjectList.get(0);
//                        }//应对连续GET，如B.filed.filed
//                        else if (tempList.getTracesRecordList.size() > 0) {
//                            h1Name = tempList.getTracesRecordList.get(0);
//                            tempList.getTracesRecordList.remove(0);
//                        } else {
////                            System.err.println("H1NAME IN GET NOT FOUND " + line);
//                        }
                        h2NewName = h1Name + "." + fieldName;
                    } else {
                        //修改静态字段
                        h2NewName = c1ClassName + "." + fieldName;
                    }
                    //添加到记录中
                    WhoPointToThisObjectCollection whoPointToThisObjectTemp = objId2objExpressionsMap.get(h2ObjId);
                    if (whoPointToThisObjectTemp == null) {
                        whoPointToThisObjectTemp = new WhoPointToThisObjectCollection(c2ClassName, h2ObjId);
                        objId2objExpressionsMap.put(h2ObjId, whoPointToThisObjectTemp);
                    }
                    whoPointToThisObjectTemp.getTracesRecordList.add(h2NewName);
                    currentProcessedLineNum += 1;
                    break;
                case "GetA":
                    //GetA H1:0x000000007ee8882a, C1:[LInner;, H2:0x00000000487d9291, C2:Inner, O:2, T:main, M:Test.f4 (LStudent;)LInner;
                    //H2 = H1[O];
                    // 获得H1的id
                    String h1ObjIdA = Utils.RegularExpressionProcess(line, "H1:(.*?),");
                    // 获得H2的id
                    String h2ObjIdA = Utils.RegularExpressionProcess(line, "H2:(.*?),");
                    // 如果获取null，不做处理
                    if (h2ObjIdA.equals("NULL")) {
                        currentProcessedLineNum += 1;
                        break;
                    }

                    //获取偏移
                    String index = Utils.RegularExpressionProcess(line, "O:(.*?),");
                    //获取h2的类名
                    String c2ClassNameA = Utils.RegularExpressionProcess(line, "C2:(.*?),");

                    // 获得H1对象的dsl表示
                    if (tryToGetDslExpressionOfObj(h1ObjIdA, objId2objExpressionsMap) == null) {
                        // 如果没有，不处理
                        currentProcessedLineNum += 1;
                        break;
                    }
//                    String h1DslExpression =

//                    WhoPointToThisObjectCollection tempList = objId2objExpressionsMap.get(h1ObjIdA);
                    String h1Name = getDslExpressionOfObj(h1ObjIdA, objId2objExpressionsMap);
//                    if (tempList == null) { // 如果tempList为null，不处理
//                        currentProcessedLineNum += 1;
//                        break;
//                    }
                    //获取h1名称
//                    if (tempList.lrAndPrPointToThisObjectList.size() > 0) {
//                        h1Name = tempList.lrAndPrPointToThisObjectList.get(0);
//                    } else if (tempList.fieldOfObjExpressionsPointToThisObjectList.size() > 0) {
//                        h1Name = tempList.fieldOfObjExpressionsPointToThisObjectList.get(0);
//                    }//应对连续GET，如B.filed.filed
//                    else if (tempList.getTracesRecordList.size() > 0) {
//                        h1Name = tempList.getTracesRecordList.get(0);
//                        tempList.getTracesRecordList.remove(0);
//                    } else {
////                        System.err.println("H1NAME IN GET NOT FOUND " + line);
//                    }
                    String h2NewNameA = h1Name + "[" + index + "]";
                    //添加到记录中
                    WhoPointToThisObjectCollection whoPointToThisObjectTempA = objId2objExpressionsMap.get(h2ObjIdA);
                    if (whoPointToThisObjectTempA == null) {
                        whoPointToThisObjectTempA = new WhoPointToThisObjectCollection(c2ClassNameA, h2ObjIdA);
                        objId2objExpressionsMap.put(h2ObjIdA, whoPointToThisObjectTempA);
                    }
                    whoPointToThisObjectTempA.getTracesRecordList.add(h2NewNameA);
                    currentProcessedLineNum += 1;
                    break;
                default:
                    System.err.println("ERROR_NOT_INCLUDE_IN_CASES " + line);
            }
        }
        return 0;
    }

    /**
     * 产生spawn语句，调用的方法的命名为 threadName + "___" + threadClassName + "___run___V"
     *
     * @param currentTextNode         当前待处理的TextNode，第一行例子 [25.483s][info][gc,heap     ] Add PR H:0x0000000076462bc3, C:MyThread, T:main, M:java/lang/Thread.start ()V
     * @param objId2objExpressionsMap 局部变量表
     * @return 产生的spawn语句
     */
    public static String genSpawnDslStatement(TextNode currentTextNode, Map<String, WhoPointToThisObjectCollection> objId2objExpressionsMap) {
        // 获得第一行内容 [25.483s][info][gc,heap     ] Add PR H:0x0000000076462bc3, C:MyThread, T:main, M:java/lang/Thread.start ()V
        String firstLineOfCurrentTextNode = currentTextNode.tracesList.get(0);
        // 获得这行Add PR的threadObjId
        String threadObjId = RegularExpressionProcess(firstLineOfCurrentTextNode, "H:(.*?),");
        // threadInfoList按顺序为[线程名称, run方法MethodName, 线程类名]
        List<String> threadInfoList = threadObjId2ThreadInfoMap.get(threadObjId);
        // 获得线程名称
        String threadName = threadInfoList.get(0);
        // 获得其线程类名
        String threadClassName = threadInfoList.get(2);

        // 获得threadObjId的dsl表示
        String threadObjDslStatement = objId2objExpressionsMap.get(threadObjId).lrAndPrPointToThisObjectList.get(0);
        // 重命名threadName
        threadName = renameThreadName(threadName);
        // 新run方法的命名为threadName + "___" + threadClassName + "___run___V"
        // 例如Thread_0___MyThread___run___V
        String newMethodName = threadName + "___" + threadClassName + "___run___V";
        String spawnDslStatement = "spawn " + newMethodName + "(" + threadObjDslStatement + ");";

        return spawnDslStatement;
    }

    /**
     * 判断当前TextNode是不是main线程调用其他线程的start方法
     *
     * @param currentTextNode 当前待判断的TextNode
     * @return
     */
    public static boolean isMainThreadCalledOtherThreadStartMethod(TextNode currentTextNode) {
        // 获得第一行内容
        String firstLineOfCurrentTextNode = currentTextNode.tracesList.get(0);
        // main线程调用其他线程的start方法的例子 [25.483s][info][gc,heap     ] Add PR H:0x0000000076462bc3, C:MyThread, T:main, M:java/lang/Thread.start ()V
        // 判断第一行是不是Add Pr操作
        String lineCommand = Utils.RegularExpressionProcess(firstLineOfCurrentTextNode, "\\[gc,heap\\s+\\] (.*?) (H|H1):");
        if ("Add PR".equals(lineCommand)) {
            // 获得这行Add PR的threadObjId
            String threadObjId = RegularExpressionProcess(firstLineOfCurrentTextNode, "H:(.*?),");
            if (threadObjId2ThreadInfoMap.keySet().contains(threadObjId)) {
                List<String> threadInfoList = threadObjId2ThreadInfoMap.get(threadObjId);
                // 获得线程名称threadObjId2ThreadInfoMap = {HashMap@1024}  size = 10
                String threadName = RegularExpressionProcess(firstLineOfCurrentTextNode, "T:(.*?),");
                // 获得start方法的methodName
                String threadStartMethodName = RegularExpressionProcess(firstLineOfCurrentTextNode, "M:(.*?)$");
                // 获得其线程类名
                String threadClassName = RegularExpressionProcess(firstLineOfCurrentTextNode, "C:(.*?),");
                threadClassName = renameClassNameReplaceSlashWithUnderlineAndRenameObjectClassName(threadClassName);
                if ("main".equals(threadName) &&
                        "java/lang/Thread.start ()V".equals(threadStartMethodName) &&
                        threadInfoList.get(2).equals(threadClassName)) {
                    return true;
                }
            }
        }
        return false;
    }


    /**
     * 处理一个MethodNode A下面的子MethodNode B，主要是在MethodNode A的DSL语句中生成调用Method B的语句 f_methodB()
     * 其中需要区分函数是否有返回值
     *
     * @param currentMethodNode                    待处理的MethodNode B节点
     * @param dslMethodContentList                 由DSL语句表示的MethodNode A 内容列表
     * @param objId2objExpressionsMap              指定对象id在DSL程序中可以怎样表示出来 <objId, expressions>
     * @param dslObjFieldToObjIdMap                给定DSL语句（对象.field 或 对象数组[index]），看看该DSL语句指向哪个对象Id，主要是帮助处理Put与PutA的trace，减少查询时间
     * @param classTypeAndNumMap                   local root命名的辅助结构， 对于新建local root例如Student stu，如何命名stu，当前采用 "student"+num的方式表示
     * @param dslCalledSubMethodName2ParameterList 表示该函数调用的子函数名称及其参数列表
     * @return 处理单个MethodNode可能跨越处理了其他TextNode的开始行，返回处理其他TextNode涉及的行数
     */
    public static int processOneSubMethodNodeInTraceToDslProcess(MethodNode currentMethodNode, List<String> dslMethodContentList,
                                                                 Map<String, WhoPointToThisObjectCollection> objId2objExpressionsMap,
                                                                 Map<String, String> dslObjFieldToObjIdMap,
                                                                 Map<String, Long> classTypeAndNumMap,
                                                                 Map<String, List<String>> dslCalledSubMethodName2ParameterList) {
        // 如果参数有在 newForMethodParameterTempLocalRootList列表中，删除 tempStu (f(new Student();),并且生成tempStu=null的DSL语句
        // 注意单独处理 新建对象初始化函数init
        // 获得MethodNode B的函数名 Test___myFunctionReturnMethod
        String currentMethodNodeName = currentMethodNode.newMethodName;
        // 看看是不是对象初始化函数，例如 Student___<init>_v1
        if (currentMethodNodeName.contains("___<init>")) {
            // 该MethodNode B是对象初始化函数
            genCallObjectInitMethodDsl(currentMethodNode, dslMethodContentList, objId2objExpressionsMap, dslCalledSubMethodName2ParameterList);
            return -1;
        } else {
            // MethodNode B不是对象初始化函数
            // 看看该函数有没有返回值
            if (!hasReturnValue(currentMethodNode)) {
                // 函数没有返回值
                genCallMethodWithNoReturnDsl(currentMethodNode, dslMethodContentList, objId2objExpressionsMap, dslCalledSubMethodName2ParameterList);
            } else {
                // 函数有返回值
                int i = genCallMethodWithReturnDsl(currentMethodNode, dslMethodContentList, objId2objExpressionsMap,
                        dslObjFieldToObjIdMap,
                        classTypeAndNumMap, dslCalledSubMethodName2ParameterList);
                return i;

            }
        }
        return 0;
    }


    /**
     * 遍历函数调用树，将每一个methodNode由DSL语句表示，表示结果放在该methodNode的dslExpressedMethod字段中
     *
     * @param methodNode 当前待处理的MethodNode
     */
    public static void translateTracesToDslOfAllMethod(MethodNode methodNode) {
        translateTracesToDslInOneMethod(methodNode);
        // 遍历该methodNode的子函数节点
        for (Node node : methodNode.contentNodeList) {
            if (node.nodeType == NodeType.METHOD) {
                translateTracesToDslOfAllMethod((MethodNode) node);
            }
        }
    }

    /**
     * 构建函数调用树
     *
     * @param srcFolderPath
     * @param threadName
     * @return
     * @throws FileNotFoundException
     */
    public static Node buildMethodCallTree(String srcFolderPath, String threadName) throws FileNotFoundException {
        String srcFilePath = srcFolderPath + threadName + ".txt";
        String removeNodeTraceFilePath = srcFolderPath + threadName + "_removedNodeTraces.txt";
        String beforeMainUsefulTracesFilePath = srcFolderPath + "beforeMainUsefulTraces.txt";

        Node rootNode = generateRenamedMethodCallTree(srcFilePath);
        // 生成的函数调用树中含有JVM内部的trace，JVM内部的trace与应用程序内存操作无关，移除JVM内部trace
        removeJvmMethodNode((MethodNode) rootNode, removeNodeTraceFilePath);
        // 合并多个相邻的TextNode为一个TextNode
        mergeContiguousTextNodes((MethodNode) rootNode);
        // 判断removeNodeTraceFilePath文件存不存在
        File file = new File(removeNodeTraceFilePath);
        if (file.exists()) {
            // 如果文件存在
            // 合并beforeMainUsefulTraces.txt和removedNodeTraces.txt两个文件
            mergeFiles(removeNodeTraceFilePath, beforeMainUsefulTracesFilePath);
        }

        return rootNode;
    }

    /**
     * 处理一个线程的traces，生成该线程的DSL程序
     * 输入经过PreProcessTraces处理过的raw trace，输出DSL程序
     *
     * @param srcFilePath 经过PreProcessTraces处理过的raw trace所在的路径
     * @param dstFilePath 输出DSL程序的路径
     * @throws FileNotFoundException
     */
    public static void genDslProgram(String srcFilePath, String dstFilePath) throws FileNotFoundException {
        // 判断dstFilePath文件是否存在，如果存在，则删除
        File file = new File(dstFilePath);
        if (file.exists()) {
            file.delete();
        }

        Node rootNode = generateRenamedMethodCallTree(srcFilePath);
        // 生成的函数调用树中含有JVM内部的trace，JVM内部的trace与应用程序内存操作无关，移除JVM内部trace
        removeJvmMethodNode((MethodNode) rootNode, srcFilePath + "removedNodeTraces.txt");
        // 合并多个相邻的TextNode为一个TextNode
        mergeContiguousTextNodes((MethodNode) rootNode);

        // 将每一个由trace描述的MethodNode转为DSL语言描述
        translateTracesToDslOfAllMethod((MethodNode) rootNode);

        // 函数精简，对相同的函数只定义一次（相同包括函数内容及其调用的函数的内容都相同）
        clearRedundantMethodDefine((MethodNode) rootNode);

        // 对每一个DSL函数体尝试生成Loop语句
        tryToGenLoopStatementsForAllMethod((MethodNode) rootNode);

        // 打印函数调用树
//        System.out.println();
//        System.out.println();
//        System.out.println("打印函数调用树");
//        printMethodCallTree(rootNode);

        // 打印生成的DSL函数，并将生成的DSL函数输出到文件
//        System.out.println();
//        System.out.println();
        System.out.println("打印生成的DSL程序，然后将DSL程序内容写入文件 dslProgram.txt");
        printAndWriteAllMethodInDsl((MethodNode) rootNode, dstFilePath);

        System.out.println();

    }

    /**
     * 读取threadInfoFile文件里的内容，保存到threadObjId2ThreadInfoMap中
     *
     * @param preProcessedTracesFolderPath ThreadInfo.txt文件 所在的文件夹路径
     * @throws FileNotFoundException
     */
    public static void loadThreadInfoToThreadObjId2ThreadInfoMap(String preProcessedTracesFolderPath) throws FileNotFoundException {
        // threadObjId2ThreadInfoMap中 key表示threadObjId，即thread对象的hashcode；value表示该线程的信息，按顺序为 [线程名称, run方法MethodName, 线程类名]
        threadObjId2ThreadInfoMap = new HashMap<>();
        // 获得ThreadInfo.txt文件 的路径
        String threadInfoFilePath = preProcessedTracesFolderPath + threadInfoFileName;
        // 判断ThreadInfo.txt文件是否存在，如果只有main线程，没有其他线程的话，ThreadInfo.txt文件不存在
        File threadInfoFile = new File(threadInfoFilePath);
        if (!threadInfoFile.exists()) {
            // 如果ThreadInfo.txt文件不存在，直接退出
            return;
        }
        // 读取threadInfoFile文件里的内容，保存到threadObjId2ThreadInfoMap中
        Scanner scanner = new Scanner(new FileReader(threadInfoFilePath));
        while (scanner.hasNextLine()) {
            String line = scanner.nextLine();
            String[] split = line.split(" \\| ");
            List<String> threadInfoList = new ArrayList<>();
            threadInfoList.add(split[1]);
            threadInfoList.add(split[2]);
            threadInfoList.add(split[3]);
            threadObjId2ThreadInfoMap.put(split[0], threadInfoList);
        }
        scanner.close();
    }

    /**
     * 处理所有线程的traces（main线程和其他线程），生成对应的MOP程序
     *
     * @param preProcessedTracesFolderPath 处理好的traces（main线程和其他线程,还有ThreadInfo.txt文件）所在的文件夹路径
     * @param mopProgramFolderPath         输出生成好的MOP程序所在的文件夹路径
     * @throws FileNotFoundException
     */
    public static void genDslProgramForAllThreads(String preProcessedTracesFolderPath, String mopProgramFolderPath) throws FileNotFoundException {
        // 如果mopProgramFolderPath存在，则删除
        deleteExsitDstFolder(mopProgramFolderPath);

        // 加载该文件夹的ThreadInfo.txt 文件内容到 MethodCallTreeGen.threadObjId2ThreadInfoMap 静态变量中
        loadThreadInfoToThreadObjId2ThreadInfoMap(preProcessedTracesFolderPath);

        // 处理main线程的trace生成MOP程序
        String srcMainThreadTraceFilePath = preProcessedTracesFolderPath + "main.txt";
        // 输出main线程MOP程序的文件路径
        String dstMainThreadMopProgramFilePath = mopProgramFolderPath + "dslProgram_main.txt";
        Node[] rootNodeArray = new Node[threadObjId2ThreadInfoMap.size() + 1];
        String[] threadNameArray = new String[rootNodeArray.length];
        int index = 0;
        rootNodeArray[index] = buildMethodCallTree(preProcessedTracesFolderPath, "main");
        threadNameArray[index++] = "main";
        // 处理每个非main线程的trace生成MOP程序
        for (String threadObjId : threadObjId2ThreadInfoMap.keySet()) {
            // 获得线程名称
            String threadName = threadObjId2ThreadInfoMap.get(threadObjId).get(0);
            rootNodeArray[index] = buildMethodCallTree(preProcessedTracesFolderPath, threadName);
            threadNameArray[index++] = threadName;
        }

        // 重新组织全局变量Traces结构，使得新建对象放在"beforeMainNewObjTraces.txt"，修改对象引用关系放在"beforeMainPutPutATraces.txt"
        extractTracesAndReformat(preProcessedTracesFolderPath + "beforeMainUsefulTraces.txt", preProcessedTracesFolderPath);
        // 获得preProcessedTracesFolderPath的上级目录
        // 获取上级目录，即debug所在的目录
        File debugFolderPath = new File(preProcessedTracesFolderPath).getParentFile();
        // 获得debug目录的路径
        String debugFolderAbsPath = debugFolderPath.getAbsolutePath();
        // 生成一张部分全局变量表，每条记录中的isUsed为false，dslExpr为null
        genPartialGlobalVarTable(preProcessedTracesFolderPath + "beforeMainNewObjTraces.txt");

        for (int i = 0; i < threadNameArray.length; i++) {
            Node rootNode = rootNodeArray[i];
            String threadName = threadNameArray[i];

            if (DslProgramGen.isDebug) {
                // 将即将生成MOP程序的traces输出到文件中
                // 遍历函数调用树，打印其trace到文件中
                writeMethodCallTreeTraces(rootNode, debugFolderAbsPath + "/readyTraces/readyTraces_" + threadName + ".txt");
            }
            // 将每一个由trace描述的MethodNode转为DSL语言描述
            translateTracesToDslOfAllMethod((MethodNode) rootNode);

            // 函数精简，对相同的函数只定义一次（相同包括函数内容及其调用的函数的内容都相同）
            clearRedundantMethodDefine((MethodNode) rootNode);

            // 对每一个DSL函数体尝试生成Loop语句
            tryToGenLoopStatementsForAllMethod((MethodNode) rootNode);

            String dstFilePath = mopProgramFolderPath + "dslProgram_" + threadName + ".txt";
            System.out.printf("打印生成的MOP程序，然后将MOP程序内容写入文件 dslProgram_%s.txt\n", threadName);
            printAndWriteAllMethodInDsl((MethodNode) rootNode, dstFilePath);
        }

        // 生成全局变量Put和PutA的DSL语句
        List<String> globalVarPutAndPutADslList = genGlobalVarPutAndPutADslStatement(preProcessedTracesFolderPath + "beforeMainPutPutATraces.txt");
        // 将全局变量写到文件中，生成Maini的静态字段和静态初始化代码块
        writeGlobalVarTable(debugFolderAbsPath + "/GlobalVar/", globalVarPutAndPutADslList);

        // 判断全局变量是否在main函数中使用
        if (isFileExsit(debugFolderAbsPath + "/GlobalVar/Main0_mainClassField.txt")) {
            // 使用了全局变量
            // 生成Maini系列类文件
            genMainiClassFile(debugFolderAbsPath + "/GlobalVar/", mopProgramFolderPath);
        }

        if (isGenOneMopProgram) {
            // 如果需要将不同线程的MOP程序合并到一起
            // 将每个非main线程的MOP程序合并到dslProgram_main.txt中去
            for (String threadObjId : threadObjId2ThreadInfoMap.keySet()) {
                // 获得线程名称
                String threadName = threadObjId2ThreadInfoMap.get(threadObjId).get(0);
                String srcThreadMopFilePath = mopProgramFolderPath + "dslProgram_" + threadName + ".txt";
                // 合并到dslProgram_main.txt中
                mergeFiles(srcThreadMopFilePath, dstMainThreadMopProgramFilePath);
                // 删除该线程的MOP程序
                File file = new File(srcThreadMopFilePath);
                if (file.exists()) {
                    file.delete();
                }
            }
            // 重命名文件dslProgram_main.txt为dslProgram.txt
            File oldFile = new File(dstMainThreadMopProgramFilePath);
            File newFile = new File(mopProgramFolderPath + "dslProgram.txt");
            oldFile.renameTo(newFile);
        }
    }

    public static void main(String[] args) throws FileNotFoundException {
        // 需要人工配置的参数，线程名称
        String threadName = "main";
        // -----
        // 注意: 在运行前需要配置一些参数
        // 参数1 表示输出为Java程序还是DSL程序，true为Java程序，false为DSL程序 "for debug"
        isGenJavaProgram = true;
        // 参数3 表示是否忽略checkTraces过程中有问题的traces "for debug"
        ignoreCheckTraceError = false;
        // 参数4 表示是否进行强制类型转换
        isCastType = true;

        long startTime = System.currentTimeMillis();
        String preProcessedTracesFolderPath = "src/main/resources/output/debug/filteredTraces/";
        String mopProgramFolderPath = "src/main/resources/debug/";
        // 加载该文件夹的ThreadInfo.txt 文件内容到 MethodCallTreeGen.threadObjId2ThreadInfoMap 静态变量中
//        loadThreadInfoToThreadObjId2ThreadInfoMap(preProcessedTracesFolderPath);

        // 读取全局变量表
        readGlobleVarTable("src/main/resources/output/debug/GlobalVar/globalVarTable.txt");

        Node rootNode = buildMethodCallTree(preProcessedTracesFolderPath, threadName);

        // 将每一个由trace描述的MethodNode转为DSL语言描述
        translateTracesToDslOfAllMethod((MethodNode) rootNode);

        // 函数精简，对相同的函数只定义一次（相同包括函数内容及其调用的函数的内容都相同）
        clearRedundantMethodDefine((MethodNode) rootNode);

        // 对每一个DSL函数体尝试生成Loop语句
        tryToGenLoopStatementsForAllMethod((MethodNode) rootNode);

        String dstFilePath = mopProgramFolderPath + "dslProgram_" + threadName + ".txt";
        System.out.printf("打印生成的MOP程序，然后将MOP程序内容写入文件 dslProgram_%s.txt\n", threadName);
        printAndWriteAllMethodInDsl((MethodNode) rootNode, dstFilePath);

        long endTime = System.currentTimeMillis();
        long executionTime = endTime - startTime;
        System.out.println("用时: " + executionTime / 1000.0 / 3600.0 + "小时");
    }
}
